package release

import (
	"context"
	"fmt"
	"testing"

	"github.com/google/go-github/v75/github"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func strPtr(s string) *string {
	return &s
}

type mockGithubReleaseService struct {
	release  *github.RepositoryRelease
	response *github.Response
	err      error
}

func (m *mockGithubReleaseService) GetLatestRelease(ctx context.Context, owner, repo string) (*github.RepositoryRelease, *github.Response, error) {
	return m.release, m.response, m.err
}

func TestGetLatestRelease(t *testing.T) {
	tests := []struct {
		name    string
		tagName string
		htmlURL string
		err     error
		want    *github.RepositoryRelease
	}{
		{
			name:    "success",
			tagName: "0.17.1",
			htmlURL: "https://github.com/flipt-io/flipt/releases/tag/0.17.2",
			err:     nil,
			want: &github.RepositoryRelease{
				TagName: strPtr("0.17.1"),
				HTMLURL: strPtr("https://github.com/flipt-io/flipt/releases/tag/0.17.2"),
			},
		},
		{
			name: "error",
			err:  fmt.Errorf("error getting release"),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			srv := &mockGithubReleaseService{
				release: &github.RepositoryRelease{
					TagName: strPtr(tt.tagName),
					HTMLURL: strPtr(tt.htmlURL),
				},
				err: tt.err,
			}

			rc := githubReleaseCheckerImpl{
				client: srv,
			}

			got, err := rc.getLatestRelease(context.Background())
			if tt.err != nil {
				assert.Equal(t, fmt.Sprintf("checking for latest version: %s", tt.err.Error()), err.Error())
				return
			}

			require.NoError(t, err)
			assert.Equal(t, tt.want, got)
		})
	}
}

type mockReleaseChecker struct {
	tagName string
	htmlURL string
	err     error
}

func (m *mockReleaseChecker) getLatestRelease(ctx context.Context) (*github.RepositoryRelease, error) {
	return &github.RepositoryRelease{
		TagName: &m.tagName,
		HTMLURL: &m.htmlURL,
	}, m.err
}

func TestCheck(t *testing.T) {
	tests := []struct {
		name    string
		version string
		tagName string
		htmlURL string
		want    Info
	}{
		{
			name:    "latest version",
			version: "0.17.1",
			tagName: "0.17.1",
			htmlURL: "",
			want: Info{
				CurrentVersion:   "0.17.1",
				LatestVersion:    "0.17.1",
				UpdateAvailable:  false,
				LatestVersionURL: "",
			},
		},
		{
			name:    "new version",
			version: "0.17.1",
			tagName: "0.17.2",
			htmlURL: "https://github.com/flipt-io/flipt/releases/tag/0.17.2",
			want: Info{
				CurrentVersion:   "0.17.1",
				LatestVersion:    "0.17.2",
				UpdateAvailable:  true,
				LatestVersionURL: "https://github.com/flipt-io/flipt/releases/tag/0.17.2",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rc := &mockReleaseChecker{
				tagName: tt.tagName,
				htmlURL: tt.htmlURL,
			}

			got, err := check(context.Background(), rc, tt.version)
			require.NoError(t, err)
			assert.Equal(t, tt.want, got)
		})
	}
}

func TestIs(t *testing.T) {
	tests := []struct {
		version string
		want    bool
	}{
		{
			version: "0.17.1",
			want:    true,
		},
		{
			version: "1.0.0",
			want:    true,
		},
		{
			version: "dev",
			want:    false,
		},
		{
			version: "1.0.0-snapshot",
			want:    false,
		},
		{
			version: "1.0.0-rc1",
			want:    false,
		},
		{
			version: "1.0.0-rc.1",
			want:    false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.version, func(t *testing.T) {
			assert.Equal(t, tt.want, Is(tt.version))
		})
	}
}
